﻿using System.Windows.Media;

namespace Red.Controls.Extensions
{
    public static class ColorExtensions
    {
        /// <summary>
        /// Code from https://github.com/RookieOne/Colors/blob/master/Colors/LightenColorExtension.cs.
        /// </summary>
        /// <summary>
        ///   This method applies lighting to a color.
        ///   For instance, a color that has a lighting factor of 1 applies, appears at its original value.
        ///   A color with a lighting factor of 0.5 appears only half as bright as it was before.
        ///   A color with a lighting factor of 1.5 appears roughly twice as bright as before.
        ///   A color with a lightning factor of 2 appears white.
        /// </summary>
        /// <param name="originalColor">Base color</param>
        /// <param name="lightFactor">
        ///   Amount of light applied to the color
        /// </param>
        /// <returns>Lit color</returns>
        /// <remarks>
        ///   This routine is very fast. Even when using it in tight loops, I (Markus) have not been able to 
        ///   meassure a significant amount of time spent in this routine (always less than 1ms). I was originally
        ///   concerened about the performance of this, so I added a caching mechanism, but that slowed things down
        ///   by 2 orders of magnitude.
        /// </remarks>
        public static Color Lighten( this Color originalColor, float lightFactor )
        {
            if ( TransformationNotNeeded( lightFactor ) )
                return originalColor;

            if ( RealBright( lightFactor ) )
                return Colors.White;

            if ( ShouldDarken( lightFactor ) )
                return DarkenColor( originalColor, lightFactor );

            return LightenColor( originalColor, lightFactor );
        }

        private static bool TransformationNotNeeded( float lightFactor )
        {
            var value = lightFactor - 1.0f;

            return value < 0.01f
                   && value > -0.01f;
        }

        private static bool RealBright( float lightFactor )
        {
            return lightFactor >= 2.0f;
        }

        private static bool ShouldDarken( float lightFactor )
        {
            return lightFactor < 1.0f;
        }

        private static Color DarkenColor( Color color, float lightFactor )
        {
            var red = ( byte )( color.R * lightFactor );
            var green = ( byte )( color.G * lightFactor );
            var blue = ( byte )( color.B * lightFactor );

            return Color.FromRgb( red, green, blue );
        }

        private static Color LightenColor( Color color, float lightFactor )
        {
            // Lighten
            // We do this by approaching 256 for a light factor of 2.0f
            float fFactor2 = lightFactor;
            if ( fFactor2 > 1.0f )
            {
                fFactor2 -= 1.0f;
            }

            var red = LightenColorComponent( color.R, fFactor2 );
            var green = LightenColorComponent( color.G, fFactor2 );
            var blue = LightenColorComponent( color.B, fFactor2 );

            return Color.FromRgb( red, green, blue );
        }

        private static byte LightenColorComponent( byte colorComponent, float fFactor )
        {
            var inverse = 255 - colorComponent;
            colorComponent += ( byte )( inverse * fFactor );

            return colorComponent < 255
                       ? colorComponent
                       : ( byte )255;
        }
    }
}
